package com.bitin.adorn.config.security;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.ArrayUtil;
import com.bitin.adorn.core.filter.RateLimiterFilter;
import com.bitin.adorn.core.security.authentication.wechat.WeChatAuthenticationProvider;
import com.bitin.adorn.core.security.exception.Auth2AccessDeniedHandler;
import com.bitin.adorn.core.security.exception.Auth2AuthenticationEntryPoint;
import com.bitin.adorn.core.security.expression.Auth2MethodSecurityExpressionHandler;
import com.bitin.adorn.core.security.filter.CaptchaValidationFilter;
import com.bitin.adorn.core.security.filter.JwtAuthenticationFilter;
import com.bitin.adorn.core.security.service.Auth2UserDetailsService;
import com.bitin.adorn.modules.system.service.ConfigService;
import com.bitin.adorn.modules.uaa.service.impl.JWTTokenService;
import com.bitin.adorn.modules.user.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * Spring Security 安全配置
 *
 * @author Mr.Lu
 * @since 2024/12/26 14:40
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
@EnableConfigurationProperties(value = {SecurityProperties.class})
public class SecurityConfig {
    private final RedisTemplate<String, Object> redisTemplate;

    private final JWTTokenService jwtTokenService;
    private final WxMaService wxMaService;
    private final UserService userService;

    private final CodeGenerator codeGenerator;
    private final ConfigService configService;
    private final SecurityProperties securityProperties;

    /**
     * 配置安全过滤链 SecurityFilterChain
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        return http
            .authorizeHttpRequests(requestMatcherRegistry -> {
                // 忽略认证的 URI 地址
                String[] ignoreUrls = securityProperties.getIgnoreUrls();
                if (ArrayUtil.isNotEmpty(ignoreUrls)) {
                    requestMatcherRegistry.requestMatchers(ignoreUrls).permitAll();
                }
                // 其他请求都需要认证
                requestMatcherRegistry.anyRequest().authenticated();
            })
            .exceptionHandling(configurer ->
                configurer
                    // 未认证异常处理器
                    .authenticationEntryPoint(new Auth2AuthenticationEntryPoint())
                    // 无权限访问异常处理器
                    .accessDeniedHandler(new Auth2AccessDeniedHandler())
            )

            // 禁用默认的 Spring Security 特性，适用于前后端分离架构
            .sessionManagement(configurer ->
                // 无状态认证，不使用 Session
                configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            // 禁用 CSRF 防护，前后端分离无需此防护机制
            .csrf(AbstractHttpConfigurer::disable)
            // 禁用默认的表单登录功能，前后端分离采用 Token 认证方式
            .formLogin(AbstractHttpConfigurer::disable)
            // 禁用 HTTP Basic 认证，避免弹窗式登录
            .httpBasic(AbstractHttpConfigurer::disable)
            // 禁用 X-Frame-Options 响应头，允许页面被嵌套到 iframe 中
            .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
            // 限流过滤器
            .addFilterBefore(new RateLimiterFilter(redisTemplate, configService), UsernamePasswordAuthenticationFilter.class)
            // 验证码校验过滤器
            .addFilterBefore(new CaptchaValidationFilter(redisTemplate, codeGenerator), UsernamePasswordAuthenticationFilter.class)
            // JWT 验证和解析过滤器
            .addFilterBefore(new JwtAuthenticationFilter(jwtTokenService), UsernamePasswordAuthenticationFilter.class)
            .build();
    }

    /**
     * 配置Web安全自定义器，以忽略特定请求路径的安全性检查。
     * <p>
     * 该配置用于指定哪些请求路径不经过Spring Security过滤器链。通常用于静态资源文件。
     */
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> {
            String[] unsecuredUrls = securityProperties.getUnsecuredUrls();
            if (ArrayUtil.isNotEmpty(unsecuredUrls)) {
                web.ignoring().requestMatchers(unsecuredUrls);
            }
        };
    }

    /**
     * 默认密码认证的 Provider
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

    /**
     * 微信认证 Provider
     */
    @Bean
    public WeChatAuthenticationProvider weChatAuthenticationProvider() {
        return new WeChatAuthenticationProvider(userService, wxMaService);
    }

    /**
     * 手动注入 AuthenticationManager，支持多种认证方式
     * - DaoAuthenticationProvider：用户名密码认证
     * - WeChatAuthenticationProvider：微信认证
     */
    @Bean
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(daoAuthenticationProvider(), weChatAuthenticationProvider());
    }

    @Bean
    protected Auth2MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
        return new Auth2MethodSecurityExpressionHandler();
    }

    @Bean
    public UserDetailsService userDetailsService(){
        return new Auth2UserDetailsService(userService);
    }

    /**
     * 密码编码器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

}
